//
//  MSDao.m
//  Musou
//
//  Created by luo danal on 4/27/12.
//  Copyright (c) 2012 __MyCompanyName__. All rights reserved.
//

#import "MSDao.h"
#import "MSPropertyUtil.h"

//typedef NSString SQL;

static FMDatabase *_fmdb = nil;

@implementation MSDao
@synthesize table = _table;
@synthesize db = _db;

#pragma mark - Class methods

+ (void)initialDBIfNil:(FMDatabase *)db{
    @synchronized(self){
        if (_fmdb == nil) {
            _fmdb = db;
        }
    }
}

+ (FMDatabase *)sharedDB{
    return _fmdb;
}

+ (BOOL)openDB{
    return [_fmdb open];
}

+ (BOOL)closeDB{
    return [_fmdb close];
}

+ (id)dao{
    return [[[self alloc] init] autorelease];
}

#pragma mark - Instance methods

- (void)dealloc{
    [_table release]; _table = nil;
    [super dealloc];
}

- (id)init{
    if ((self = [super init])) {
        
    }
    return self;
}

- (void)setTable:(NSString *)table{
    [_table release];
    _table = [table copy];
    if (![self tableExists]) {
        [self createTable];
    }
}

- (FMDatabase *)db{
    return _fmdb;
}

- (BOOL)tableExists{
    NSString *query = [NSString stringWithFormat:@"SELECT `name` FROM `sqlite_master` WHERE `type`='table' AND `name`='%@'", self.table];
    FMResultSet *rs = [_fmdb executeQuery:query];
    [rs next];
    return [rs stringForColumnIndex:0] != nil; 
}

//Should be overrided
- (BOOL)createTable{
    return NO;
}

- (BOOL)addOneRow:(id)unit{

    return NO;
}

- (id)rowForCondition:(NSString *)condition{
    return nil;    
}

- (BOOL)addList:(NSArray *)unitList{
    return NO;
}

- (NSArray *)readListForCondition:(NSString *)condition{
    return nil;
}

- (BOOL)removeForCondition:(NSString *)condition{
    NSString *sql = [NSString stringWithFormat:@"DELETE FROM `%@`",self.table];
    if (condition != nil) {
        sql = [sql stringByAppendingFormat:@" WHERE %@",condition];
    }
    BOOL bl = [_fmdb executeUpdate:sql];
    if(!bl){
        NSLog(@"%@",[_fmdb lastErrorMessage]);
    }
    return bl;
}

- (BOOL)update:(NSString *)update forCondition:(NSString *)condition{
    NSString *sql = [NSString stringWithFormat:@"UPDATE `%@` SET %@",self.table,update];
    if (condition != nil) {
        sql = [sql stringByAppendingFormat:@" WHERE %@",condition];
    }
    BOOL bl = [_fmdb executeUpdate:sql];
    if(!bl){
        NSLog(@"%@",[_fmdb lastErrorMessage]);
    }
    return bl;
}

///////////
- (BOOL)add{
    NSMutableString *inserts = [[NSMutableString alloc] init];
    NSMutableString *values = [[NSMutableString alloc] init];
    
    NSDictionary *pts = [MSPropertyUtil classPropsFor:[self class]];
    NSEnumerator *enumerator = [pts keyEnumerator];
    id key = nil, type = nil, val = nil;
    while (key = [enumerator nextObject]) {
        type = [pts objectForKey:key];
//        val = [self performSelector:NSSelectorFromString(key)];
        val = [self valueForKey:key];
        [inserts appendFormat:@"%@,",key];
        if ([type isEqualToString:@"i"]) {
            [values appendFormat:@"%d,",[val intValue]];
        }
        else if ([type isEqualToString:@"l"]) {
            [values appendFormat:@"%ld,",[val longValue]];
        } 
        else if ([type isEqualToString:@"f"]) {
            [values appendFormat:@"%f,",[val floatValue]];
        }
        else {
            [values appendFormat:@"'%@',",val];
        }
    }
    NSString *sql = [NSString stringWithFormat:@"INSERT INTO `%@` (%@) VALUES (%@)",
                     self.table,
                     [inserts substringToIndex:[inserts length] - 1],
                     [values substringToIndex:[values length] - 1]];
    [inserts release];
    [values release];
//    NSLog(@"%@",sql);
    BOOL bl = [self.db executeUpdate:sql];
    if (!bl) {
        NSLog(@"%@",[self.db lastErrorMessage]);
    }
    return bl;
}

- (BOOL)remove{
    NSString *sql = [NSString stringWithFormat:@"DELETE FROM `%@` WHERE `rowid` = %d",self.table,_rowid];
    return [self.db executeUpdate:sql];
}

- (BOOL)update{
    NSMutableString *sql = [[NSMutableString alloc] initWithFormat:@"UPDATE `%@` SET ",self.table];
    NSDictionary *pts = [MSPropertyUtil classPropsFor:[self class]];
    NSEnumerator *enumerator = [pts keyEnumerator];
    id key = nil, type = nil, val = nil;
    while (key = [enumerator nextObject]) {
        type = [pts objectForKey:key];
        val = [self valueForKey:key];
        //        NSLog(@"%@ %@",val,[val class]);
        if (val != nil) {   //It's a condition
            
            if ([type isEqualToString:@"i"]) {
                [sql appendFormat:@" `%@` = %d ,",key,[val intValue]];
            }
            else if ([type isEqualToString:@"l"]) {
                [sql appendFormat:@" `%@` = %ld ,",key,[val longValue]];
            } 
            else if ([type isEqualToString:@"f"]) {
                [sql appendFormat:@" `%@` = %f ,",key,[val floatValue]];
            }
            else {
                [sql appendFormat:@" `%@` = '%@' ,",key,val];
            }
        }
    }
    NSString *sql2 = [NSString stringWithFormat:@"%@ WHERE `rowid` = %d",
                      [sql substringToIndex:[sql length] - 1], _rowid];
    [sql release];
    BOOL bl = [self.db executeUpdate:sql2];
    if (!bl) {
        NSLog(@"%@",[self.db lastErrorMessage]);
    }
    return bl;
}

- (void)read{
    NSMutableString *sql = [[NSMutableString alloc] init];
    [sql appendFormat:@"SELECT *,rowid FROM `%@` WHERE",self.table];
//    while (key = [enumerator nextObject]) {
//        type = [pts objectForKey:key];
//        val = [self valueForKey:key];
//        NSLog(@"%@ %@",val,[val class]);
//        if (val != nil) {   //It's a condition
//            
//            if ([type isEqualToString:@"i"]) {
//                [sql appendFormat:@" `%@` = %d AND",key,[val intValue]];
//            }
//            else if ([type isEqualToString:@"l"]) {
//                [sql appendFormat:@" `%@` = %ld AND",key,[val longValue]];
//            } 
//            else if ([type isEqualToString:@"f"]) {
//                [sql appendFormat:@" `%@` = %f AND",key,[val floatValue]];
//            }
//            else {
//                [sql appendFormat:@" `%@` = '%@' AND",key,val];
//            }
//        }
//    }
    [sql appendString:@" 1 LIMIT 1"];
    FMResultSet *rs = [self.db executeQuery:sql];
    NSDictionary *pts = [MSPropertyUtil classPropsFor:[self class]];
    NSEnumerator *enumerator = [pts keyEnumerator];
    id key = nil, type = nil, val = nil;
    if([rs next]){
        NSString *sel = @"set";
        NSString *c = nil;  //First char
        _rowid = [rs intForColumn:@"rowid"];
                                                NSLog(@"ROWID:%d",_rowid);  ////////
        while (key = [enumerator nextObject]) {
            type = [pts objectForKey:key];
//            val = [self valueForKey:key];
            c = [[key substringToIndex:1] uppercaseString];
            sel = [NSString stringWithFormat:@"set%@:",[key stringByReplacingCharactersInRange:NSMakeRange(0, 1) withString:c]];

            if ([type isEqualToString:@"i"]) {
                int ival = [rs intForColumn:key];  //[val intValue];
                [self invokeSelector:NSSelectorFromString(sel) argument:&ival target:self];
            }
            else if ([type isEqualToString:@"l"]) {
                long lval = [rs longForColumn:key];    //[val longValue];
                [self invokeSelector:NSSelectorFromString(sel) argument:&lval target:self];
            } 
            else if ([type isEqualToString:@"f"]) {
                float fval = [rs doubleForColumn:key];  //[val floatValue];
                [self invokeSelector:NSSelectorFromString(sel) argument:&fval target:self];
            }
            else {
                val = [rs stringForColumn:key];
                [self invokeSelector:NSSelectorFromString(sel) argument:&val target:self];
            }
        }

    }
    [sql release];
//    return self; 
}

- (NSArray *)readAll{
    
    //TODO
    NSString *sql = [NSString stringWithFormat:@"SELECT *,rowid FROM `%@`",self.table];
    FMResultSet *rs = [self.db executeQuery:sql];
    NSMutableArray *list = [NSMutableArray array];
    NSDictionary *pts = [MSPropertyUtil classPropsFor:[self class]];
    id key = nil, type = nil, val = nil;
    NSString *sel = @"set";
    NSString *c = nil;  //First char
    
    while ([rs next]) {
                    NSLog(@"ROWID:%d",[rs intForColumn:@"rowid"]);  ////////
        id dao = [[[self class] alloc] init];
        NSEnumerator *enumerator = [pts keyEnumerator];
        while (key = [enumerator nextObject]) {
            type = [pts objectForKey:key];
//            val = [self valueForKey:key];
            c = [[key substringToIndex:1] uppercaseString];
            sel = [NSString stringWithFormat:@"set%@:",[key stringByReplacingCharactersInRange:NSMakeRange(0, 1) withString:c]];
  
            if ([type isEqualToString:@"i"]) {
                int ival = [rs intForColumn:key];   //[val intValue];
                [self invokeSelector:NSSelectorFromString(sel) argument:&ival target:dao];
            }
            else if ([type isEqualToString:@"l"]) {
                long lval = [rs longForColumn:key];   //[val longValue];
                [self invokeSelector:NSSelectorFromString(sel) argument:&lval target:dao];
            } 
            else if ([type isEqualToString:@"f"]) {
                float fval = [rs doubleForColumn:key];   //[val floatValue];
                [self invokeSelector:NSSelectorFromString(sel) argument:&fval target:dao];
            }
            else {
                val = [rs stringForColumn:key];
                [self invokeSelector:NSSelectorFromString(sel) argument:&val target:dao];
            }
        }
        [list addObject:dao];
        [dao release];
    }
    
    return list;
}

- (BOOL)removeAll{
    NSString *sql = [NSString stringWithFormat:@"DELETE FROM `%@`",self.table];
    return [self.db executeUpdate:sql];
}

#pragma mark - Privite 
- (void)invokeSelector:(SEL)selector argument:(void *)arg target:(id)target{
//    NSLog(@"%s",selector);
    NSMethodSignature *sign = [target methodSignatureForSelector:selector];
    NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:sign];
    [invocation setArgument:arg atIndex:2];
    [invocation setTarget:target];
    [invocation setSelector:selector];
    [invocation invoke];
}
@end
